diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-11-20 12:27:46 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-20 19:32:09 +0100 |
| commit | 70b4f633a6fbedb58c8b9134ac64ede854d489de (patch) | |
| tree | c757bb12ad9a588e23b25cdb8b46710ac14dbcb1 /src/pages/article/[slug].tsx | |
| parent | 9a481f066e1427d53a06cf7aeec525a745abf03f (diff) | |
refactor(components): replace PageLayout template with Page
* split pages in smaller components (it is both easier to maintain and
more readable, we avoid the use of fragments in pages directory)
* extract breadcrumbs from article tag (the navigation is not related
to the page contents)
* remove useReadingTime hook
* remove layout options except `isHome`
Diffstat (limited to 'src/pages/article/[slug].tsx')
| -rw-r--r-- | src/pages/article/[slug].tsx | 216 |
1 files changed, 79 insertions, 137 deletions
diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx index 0cba7a6..224b1c5 100644 --- a/src/pages/article/[slug].tsx +++ b/src/pages/article/[slug].tsx @@ -2,24 +2,23 @@ import type { ParsedUrlQuery } from 'querystring'; import type { GetStaticPaths, GetStaticProps } from 'next'; import Head from 'next/head'; -import NextImage from 'next/image'; import { useRouter } from 'next/router'; import Script from 'next/script'; -import type { HTMLAttributes } from 'react'; import { useIntl } from 'react-intl'; import type { Comment as CommentSchema, WithContext } from 'schema-dts'; import { - ButtonLink, getLayout, - Link, - PageLayout, SharingWidget, Spinner, - Time, type CommentData, Heading, - MetaList, - MetaItem, + Page, + PageHeader, + PageBody, + PageFooter, + PageComments, + PageSidebar, + TocWidget, } from '../../components'; import { getAllArticlesSlugs, @@ -41,8 +40,8 @@ import { useArticle, useBreadcrumb, useComments, + useHeadingsTree, usePrism, - useReadingTime, } from '../../utils/hooks'; type ArticlePageProps = { @@ -84,7 +83,6 @@ const ArticlePage: NextPageWithLayout<ArticlePageProps> = ({ title: article?.title ?? '', url: `${ROUTES.ARTICLE}/${slug}`, }); - const readingTime = useReadingTime(article?.meta.wordsCount ?? 0, true); const { attributes, className } = usePrism({ attributes: { 'data-toolbar-order': 'show-language,copy-to-clipboard,color-scheme', @@ -107,11 +105,21 @@ const ArticlePage: NextPageWithLayout<ArticlePageProps> = ({ description: 'ArticlePage: loading article message', id: '4iYISO', }); + const { ref, tree } = useHeadingsTree({ fromLevel: 2 }); if (isFallback || !article) return <Spinner>{loadingArticle}</Spinner>; const { content, id, intro, meta, title } = article; - const { author, commentsCount, cover, dates, seo, thematics, topics } = meta; + const { + author, + commentsCount, + cover, + dates, + seo, + thematics, + topics, + wordsCount, + } = meta; const webpageSchema = getWebPageSchema({ description: intro, @@ -211,9 +219,15 @@ const ArticlePage: NextPageWithLayout<ArticlePageProps> = ({ id: 'HKKkQk', description: 'SharingWidget: widget title', }); + const tocTitle = intl.formatMessage({ + defaultMessage: 'Table of Contents', + description: 'PageLayout: table of contents title', + id: 'eys2uX', + }); + const articleComments = getComments(commentsData); return ( - <> + <Page breadcrumbs={breadcrumbItems}> <Head> <title>{seo.title}</title> {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */} @@ -231,135 +245,63 @@ const ArticlePage: NextPageWithLayout<ArticlePageProps> = ({ // eslint-disable-next-line react/no-danger -- Necessary for schema dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} /> - <PageLayout - allowComments={true} - bodyAttributes={attributes as HTMLAttributes<HTMLDivElement>} - bodyClassName={styles.body} - breadcrumb={breadcrumbItems} - breadcrumbSchema={breadcrumbSchema} - comments={getComments(commentsData)} - footerMeta={ - topics ? ( - <MetaList> - <MetaItem - hasInlinedValues - label={intl.formatMessage({ - defaultMessage: 'Read more articles about:', - description: 'ArticlePage: footer topics list label', - id: '50xc4o', - })} - value={topics.map((topic) => { - return { - id: `topic--${topic.id}`, - value: ( - <ButtonLink - className={styles.btn} - key={topic.id} - to={topic.url} - > - {topic.logo ? <NextImage {...topic.logo} /> : null}{' '} - {topic.name} - </ButtonLink> - ), - }; - })} - /> - </MetaList> - ) : undefined - } - headerMeta={ - <MetaList> - {author ? ( - <MetaItem - isInline - label={intl.formatMessage({ - defaultMessage: 'Written by:', - description: 'ArticlePage: author label', - id: 'MJbZfX', - })} - value={author.name} - /> - ) : null} - <MetaItem - isInline - label={intl.formatMessage({ - defaultMessage: 'Published on:', - description: 'Page: publication date label', - id: '4QbTDq', - })} - value={<Time date={dates.publication} />} - /> - {dates.update ? ( - <MetaItem - isInline - label={intl.formatMessage({ - defaultMessage: 'Updated on:', - description: 'Page: update date label', - id: 'Ez8Qim', - })} - value={<Time date={dates.update} />} - /> - ) : null} - <MetaItem - isInline - label={intl.formatMessage({ - defaultMessage: 'Reading time:', - description: 'ArticlePage: reading time label', - id: 'Gw7X3x', - })} - value={readingTime} - /> - {thematics ? ( - <MetaItem - isInline - label={intl.formatMessage({ - defaultMessage: 'Thematics:', - description: 'ArticlePage: thematics meta label', - id: 'CvOqoh', - })} - value={thematics.map((thematic) => { - return { - id: `thematic-${thematic.id}`, - value: ( - <Link key={thematic.id} href={thematic.url}> - {thematic.name} - </Link> - ), - }; - })} - /> - ) : null} - </MetaList> - } - id={id as number} + <Script + dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }} + // eslint-disable-next-line react/jsx-no-literals -- Id allowed + id="schema-breadcrumb" + type="application/ld+json" + /> + <PageHeader + heading={title} intro={intro} - title={title} - withToC={true} - widgets={[ - <SharingWidget - // eslint-disable-next-line react/jsx-no-literals -- Key allowed - key="sharing-widget" - className={styles.widget} - data={{ excerpt: intro, title, url: pageUrl }} - heading={<Heading level={3}>{sharingWidgetTitle}</Heading>} - media={[ - 'diaspora', - 'email', - 'facebook', - 'journal-du-hacker', - 'linkedin', - 'twitter', - ]} - />, - ]} - > - {contentWithPrismClasses} - </PageLayout> - </> + meta={{ + author: author?.name, + publicationDate: dates.publication, + thematics, + updateDate: dates.update, + wordsCount, + }} + /> + <PageSidebar> + <TocWidget + heading={<Heading level={3}>{tocTitle}</Heading>} + tree={tree} + /> + </PageSidebar> + <PageBody + {...attributes} + className={styles.body} + dangerouslySetInnerHTML={{ __html: contentWithPrismClasses }} + ref={ref} + /> + {topics ? <PageFooter readMoreAbout={topics} /> : null} + <PageSidebar> + <SharingWidget + // eslint-disable-next-line react/jsx-no-literals -- Key allowed + key="sharing-widget" + className={styles.widget} + data={{ excerpt: intro, title, url: pageUrl }} + heading={<Heading level={3}>{sharingWidgetTitle}</Heading>} + media={[ + 'diaspora', + 'email', + 'facebook', + 'journal-du-hacker', + 'linkedin', + 'twitter', + ]} + /> + </PageSidebar> + <PageComments + comments={articleComments ?? []} + depth={2} + pageId={id as number} + /> + </Page> ); }; -ArticlePage.getLayout = (page) => getLayout(page, { useGrid: true }); +ArticlePage.getLayout = (page) => getLayout(page); type PostParams = { slug: string; |
